package com.laolang.shop.modules.auth.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.laolang.shop.common.core.consts.ModuleNameConst;
import com.laolang.shop.common.core.util.CommonUtil;
import com.laolang.shop.common.core.util.LogUtil;
import com.laolang.shop.modules.auth.config.JwtProperty;
import com.laolang.shop.modules.auth.consts.AuthConst;
import com.laolang.shop.modules.auth.entity.TokenUser;
import com.laolang.shop.modules.auth.util.JwtTokenUtil;
import com.laolang.shop.modules.sys.entity.SysRole;
import com.laolang.shop.modules.sys.service.SysUserRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author khlbat
 * @version 1.0
 * @date 2020/11/1 20:46
 */
@Slf4j
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    private final JwtProperty jwtProperty;

    private final JwtTokenUtil jwtTokenUtil;

    private final SysUserRoleService sysUserRoleService;

    private final RedisTemplate<String, Object> redisTemplate;

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager, JwtProperty jwtProperty, JwtTokenUtil jwtTokenUtil,
                                  SysUserRoleService sysUserRoleService, RedisTemplate<String, Object> redisTemplate) {
        super(authenticationManager);
        this.jwtProperty = jwtProperty;
        this.jwtTokenUtil = jwtTokenUtil;
        this.sysUserRoleService = sysUserRoleService;
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String token = CommonUtil.getTokenWithoutBearer(request.getHeader(jwtProperty.getHeader()));
        if (StrUtil.isBlank(token)) {
            chain.doFilter(request, response);
            return;
        }
        SecurityContextHolder.getContext().setAuthentication(getAuthentication(token));
        super.doFilterInternal(request, response, chain);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(String token) {
        TokenUser tokenUser = jwtTokenUtil.getAuthUser(token);
        if (null != token) {
            return new UsernamePasswordAuthenticationToken(tokenUser, tokenUser.getPassword(),
                    getAuthorities(tokenUser.getUserId()));
        }
        return null;
    }

    private Collection<? extends GrantedAuthority> getAuthorities(Long userId) {
        Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
        String key = AuthConst.getAuthoritiesKey(userId);

        try {
            List<Object> range = redisTemplate.opsForList().range(key, 0, -1);
            if (CollUtil.isNotEmpty(range)) {
                authorities = range.stream().map(o -> new SimpleGrantedAuthority(o.toString())).collect(Collectors.toList());
            }
        } catch (Exception e) {
            LogUtil.warn(log, ModuleNameConst.AUTH, StrUtil.format("从 redis 获取用户: {} 权限失败,msg:{}", userId, e.getMessage()));
        }

        List<SysRole> roles = sysUserRoleService.listByUserId(userId);
        if (CollUtil.isNotEmpty(roles)) {
            authorities = roles.stream().map(sysRole -> new SimpleGrantedAuthority(sysRole.getCode())).collect(Collectors.toList());
        }

        try {
            redisTemplate.delete(key);
            if (CollUtil.isNotEmpty(authorities)) {
                redisTemplate.opsForList().rightPushAll(key, authorities.stream()
                        .map((Function<GrantedAuthority, String>) GrantedAuthority::getAuthority).toArray());
            }
        } catch (Exception e) {
            LogUtil.warn(log, ModuleNameConst.AUTH, StrUtil.format("保存用户: {} 权限至 redis 失败,msg:{}", userId, e.getMessage()));
        }

        return authorities;
    }


}
